import 'package:dk_flutter_base/dk_ui.dart';
import 'package:flutter/rendering.dart';
import 'dart:math' as math;

///带行数限制的流式布局
class LineLimitFlow extends MultiChildRenderObjectWidget {
  final EdgeInsets padding;
  final double spacing;
  final double runSpacing;
  final int maxLine;

  LineLimitFlow(
      {Key key,
      this.padding = const EdgeInsets.all(0),
      this.spacing = 10,
      this.runSpacing = 10,
      this.maxLine = 3,
      List children = const []})
      : assert(padding != null),
        super(key: key, children: RepaintBoundary.wrapAll(children));

  @override
  RenderObject createRenderObject(BuildContext context) {
    return LineLimitRenderFlow(
        padding: padding,
        spacing: spacing,
        runSpacing: runSpacing,
        maxLine: maxLine);
  }

  @override
  void updateRenderObject(
      BuildContext context, LineLimitRenderFlow renderObject) {
    renderObject
      ..padding = padding
      ..spacing = spacing
      ..runSpacing = runSpacing
      ..maxLine = maxLine;
  }
}

///每个child都带一个parentData,在这里可以定义想用的属性
class _LineLimitFlowParentData extends ContainerBoxParentData<RenderBox> {
  //是否可用
  bool _dirty = false;
}

///主要实现
class LineLimitRenderFlow extends RenderBox
    with
        ContainerRenderObjectMixin<RenderBox, _LineLimitFlowParentData>,
        RenderBoxContainerDefaultsMixin<RenderBox, _LineLimitFlowParentData> {
  EdgeInsets _padding;

  set padding(EdgeInsets padding) {
    if (padding == null) {
      return;
    }
    this._padding = padding;
  }

  double _spacing;

  set spacing(double spacing) {
    if (spacing == null) {
      return;
    }
    this._spacing = spacing;
  }

  double _runSpacing;

  set runSpacing(double runSpacing) {
    if (runSpacing == null) {
      return;
    }
    this._runSpacing = runSpacing;
  }

  int _maxLine;

  set maxLine(int maxLine) {
    if (maxLine == null) {
      return;
    }
    this._maxLine = maxLine;
  }

  LineLimitRenderFlow(
      {EdgeInsets padding = const EdgeInsets.all(0),
      double spacing = 10,
      double runSpacing = 10,
      int maxLine = 3})
      : assert(padding != null),
        _padding = padding,
        _spacing = spacing,
        _runSpacing = runSpacing,
        _maxLine = maxLine;

  @override
  bool get isRepaintBoundary => true;

  @override
  void setupParentData(RenderBox child) {
    if (child.parentData is! _LineLimitFlowParentData)
      child.parentData = _LineLimitFlowParentData();
  }

  //核心方法，计算每个child的offset,也就是想对于原点的偏移位置，最终算出来满足条件的要参与layout和paint的children,
  //然后根据要显示的children的高度，算出窗口高度。
  //不参与显示的child打上_dirty=ture的标记。
  double _computeIntrinsicHeightForWidth(double width) {
    int runCount = 0;
    double height = _padding.top;
    double runWidth = _padding.left;
    double runHeight = 0.0;
    int childCount = 0;
    RenderBox child = firstChild;
    while (child != null) {
      final double childWidth = child.getMaxIntrinsicWidth(double.infinity);
      final double childHeight = child.getMaxIntrinsicHeight(childWidth);
      final _LineLimitFlowParentData childParentData = child.parentData;
      if (runWidth + childWidth + _padding.right > width) {
        if (_maxLine > 0 && runCount + 1 == _maxLine) {
          childParentData._dirty = true;
          child = childAfter(child);
          continue;
        }
        childParentData._dirty = false;
        height += runHeight;
        if (runCount > 0) {
          height += _runSpacing;
        }
        runCount += 1;
        runWidth = _padding.left;
        runHeight = 0.0;
        childCount = 0;
      }
      //更新绘制位置start
      childParentData.offset = Offset(
          runWidth + ((childCount > 0) ? _spacing : 0),
          height + ((runCount > 0) ? _runSpacing : 0));
      //更新绘制位置end
      runWidth += childWidth;
      runHeight = math.max(runHeight, childHeight);
      if (childCount > 0) {
        runWidth += _spacing;
      }
      childCount += 1;
      child = childAfter(child);
    }
    if (childCount > 0) {
      height += runHeight + _runSpacing + _padding.bottom;
    }
    return height;
  }

  //因为是纵向换行，横向固定使用父控限定的最大宽度
  double _computeIntrinsicWidthForHeight(double height) {
    return constraints.maxWidth;
  }

  @override
  double computeMinIntrinsicWidth(double height) {
    double width = _computeIntrinsicWidthForHeight(height);
    return width;
  }

  @override
  double computeMaxIntrinsicWidth(double height) {
    double width = _computeIntrinsicWidthForHeight(height);
    return width;
  }

  @override
  double computeMinIntrinsicHeight(double width) {
    double height = _computeIntrinsicHeightForWidth(width);
    return height;
  }

  @override
  double computeMaxIntrinsicHeight(double width) {
    double height = _computeIntrinsicHeightForWidth(width);
    return height;
  }

  @override
  void performLayout() {
    RenderBox child = firstChild;
    if (child == null) {
      size = constraints.smallest;
      return;
    }
    size = Size(_computeIntrinsicWidthForHeight(constraints.maxHeight),
        _computeIntrinsicHeightForWidth(constraints.maxWidth));

    //布局每个child,_dirty的child自动忽略
    while (child != null) {
      final BoxConstraints innerConstraints = constraints.loosen();
      final _LineLimitFlowParentData childParentData = child.parentData;
      if (!childParentData._dirty) {
        child.layout(innerConstraints, parentUsesSize: true);
      }
      child = childParentData.nextSibling;
    }
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    RenderBox child = firstChild;
    //绘制每个child
    while (child != null) {
      final _LineLimitFlowParentData childParentData = child.parentData;
      if (!childParentData._dirty) {
        context.paintChild(child, childParentData.offset + offset);
      }
      child = childParentData.nextSibling;
    }
  }

  @override
  bool hitTestChildren(HitTestResult result, {Offset position}) {
    //响应点击区域，因为布局和绘制是同样的位置 ，没有偏移，所以使用默认逻辑
    return defaultHitTestChildren(result, position: position);
  }
}
